1 /*!
2  * jQuery Form Plugin
3  * version:
3.24 (26-DEC-2012)
4  * @requires jQuery v1.
5 or later
5  *
6  * Examples and documentation at: http://malsup.com/jquery/form/
7  * Project repository: https://github.com/malsup/form
8  * Dual licensed under the MIT and GPL licenses:
9  * http://malsup.github.com/mit-license.txt
10  * http://malsup.github.com/gpl-license-v2.txt
11  */
12 /*
global ActiveXObject alert */
13 ;(function($) {

14 "use strict"
;
15
16 /*
17     Usage Note:
18     -----------
19     Do not use both ajaxSubmit and ajaxForm
on the same form. These
20     functions are mutually exclusive. Use ajaxSubmit
if you want
21     to bind your own submit handler to the form. For example,
22
23     $(document).ready(function() {
24         $(
'#myForm').on('submit', function(e) {
25             e.preventDefault(); // <-- important
26             $(
this).ajaxSubmit({
27                 target:
'#output'
28             });
29         });
30     });
31
32     Use ajaxForm
when you want the plugin to manage all the event binding
33     
for you. For example,
34
35     $(document).ready(function() {
36         $(
'#myForm').ajaxForm({
37             target:
'#output'
38         });
39     });
40     
41     You can also use ajaxForm with delegation (requires jQuery v1.
7+), so the
42     form does not have to exist
when you invoke ajaxForm:
43
44     $(
'#myForm').ajaxForm({
45         delegation:
true,
46         target:
'#output'
47     });
48     
49     When
using ajaxForm, the ajaxSubmit function will be invoked for you
50     at the appropriate time.
51 */
52
53 /**
54  * Feature detection
55  */

56 var
feature = {};
57 feature.fileapi = $(
"<input type='file'/>").get(0).files !== undefined;
58 feature.formdata = window.FormData !== undefined;

59
60 /**
61  * ajaxSubmit() provides a mechanism
for immediately submitting
62  * an HTML form
using AJAX.
63  */

64 $.fn.ajaxSubmit = function(options) {
65     
/*jshint scripturl:true */
66
67     
// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
68     
if (!this.length) {
69         log(
'ajaxSubmit: skipping submit process - no element selected');
70         
return this;
71     }
72     
73     
var method, action, url, $form = this;
74
75     
if (typeof options == 'function') {
76         options = { success: options };
77     }
78
79     method =
this.attr('method');
80     action =
this.attr('action');
81     url = (
typeof action === 'string') ? $.trim(action) : '';
82     url = url || window.location.href ||
'';
83     
if (url) {
84         
// clean url (don't include hash vaue)
85         url = (url.match(/^([^#]+)/)||[])[
1];
86     }
87
88     options = $.extend(
true, {
89         url: url,
90         success: $.ajaxSettings.success,
91         type: method ||
'GET',
92         iframeSrc: /^https/i.test(window.location.href ||
'') ? 'javascript:false' : 'about:blank'
93     }, options);
94
95     
// hook for manipulating the form data before it is extracted;
96     
// convenient for use with rich editors like tinyMCE or FCKEditor
97     
var veto = {};
98     
this.trigger('form-pre-serialize', [this, options, veto]);
99     
if (veto.veto) {
100         log(
'ajaxSubmit: submit vetoed via form-pre-serialize trigger');
101         
return this;
102     }
103
104     
// provide opportunity to alter form data before it is serialized
105     
if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
106         log(
'ajaxSubmit: submit aborted via beforeSerialize callback');
107         
return this;
108     }
109
110     
var traditional = options.traditional;
111     
if ( traditional === undefined ) {
112         traditional = $.ajaxSettings.traditional;
113     }
114     
115     
var elements = [];
116     
var qx, a = this.formToArray(options.semantic, elements);
117     
if (options.data) {
118         options.extraData = options.data;
119         qx = $.param(options.data, traditional);
120     }
121
122     
// give pre-submit callback an opportunity to abort the submit
123     
if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
124         log(
'ajaxSubmit: submit aborted via beforeSubmit callback');
125         
return this;
126     }
127
128     
// fire vetoable 'validate' event
129     
this.trigger('form-submit-validate', [a, this, options, veto]);
130     
if (veto.veto) {
131         log(
'ajaxSubmit: submit vetoed via form-submit-validate trigger');
132         
return this;
133     }
134
135     
var q = $.param(a, traditional);
136     
if (qx) {
137         q = ( q ? (q +
'&' + qx) : qx );
138     }
139     
if (options.type.toUpperCase() == 'GET') {
140         options.url += (options.url.indexOf(
'?') >= 0 ? '&' : '?') + q;
141         options.data =
null; // data is null for 'get'
142     }
143     
else {
144         options.data = q;
// data is the query string for 'post'
145     }
146
147     
var callbacks = [];
148     
if (options.resetForm) {
149         callbacks.push(function() { $form.resetForm(); });
150     }
151     
if (options.clearForm) {
152         callbacks.push(function() { $form.clearForm(options.includeHidden); });
153     }
154
155     
// perform a load on the target only if dataType is not provided
156     
if (!options.dataType && options.target) {
157         
var oldSuccess = options.success || function(){};
158         callbacks.push(function(data) {
159             
var fn = options.replaceTarget ? 'replaceWith' : 'html';
160             $(options.target)[fn](data).each(oldSuccess, arguments);
161         });
162     }
163     
else if (options.success) {
164         callbacks.push(options.success);
165     }
166
167     options.success = function(data, status, xhr) {
// jQuery 1.4+ passes xhr as 3rd arg
168         
var context = options.context || this ; // jQuery 1.4+ supports scope context
169         
for (var i=0, max=callbacks.length; i < max; i++) {
170             callbacks[i].apply(context, [data, status, xhr || $form, $form]);
171         }
172     };
173
174     
// are there files to upload?
175
176     
// [value] (issue #113), also see comment:
177     
// https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
178     
var fileInputs = $('input[type=file]:enabled[value!=""]', this);
179
180     
var hasFileInputs = fileInputs.length > 0;
181     
var mp = 'multipart/form-data';
182     
var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
183
184     
var fileAPI = feature.fileapi && feature.formdata;
185     log(
"fileAPI :" + fileAPI);
186     
var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
187
188     
var jqxhr;
189
190     
// options.iframe allows user to force iframe mode
191     
// 06-NOV-09: now defaulting to iframe mode if file input is detected
192     
if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
193         
// hack to fix Safari hang (thanks to Tim Molendijk for this)
194         
// see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
195         
if (options.closeKeepAlive) {
196             $.
get(options.closeKeepAlive, function() {
197                 jqxhr = fileUploadIframe(a);
198             });
199         }
200         
else {
201             jqxhr = fileUploadIframe(a);
202         }
203     }
204     
else if ((hasFileInputs || multipart) && fileAPI) {
205         jqxhr = fileUploadXhr(a);
206     }
207     
else {
208         jqxhr = $.ajax(options);
209     }
210
211     $form.removeData(
'jqxhr').data('jqxhr', jqxhr);
212
213     
// clear element array
214     
for (var k=0; k < elements.length; k++)
215         elements[k] =
null;
216
217     
// fire 'notify' event
218     
this.trigger('form-submit-notify', [this, options]);
219     
return this;
220
221     
// utility fn for deep serialization
222     function deepSerialize(extraData){
223         
var serialized = $.param(extraData).split('&');
224         
var len = serialized.length;
225         
var result = {};
226         
var i, part;
227         
for (i=0; i < len; i++) {
228             
// #252; undo param space replacement
229             serialized[i] = serialized[i].replace(/\+/g,
' ');
230             part = serialized[i].split(
'=');
231             result[decodeURIComponent(part[
0])] = decodeURIComponent(part[1]);
232         }
233         
return result;
234     }
235
236      
// XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
237     function fileUploadXhr(a) {
238         
var formdata = new FormData();
239
240         
for (var i=0; i < a.length; i++) {
241             formdata.append(a[i].name, a[i].
value);
242         }
243
244         
if (options.extraData) {
245             
var serializedData = deepSerialize(options.extraData);
246             
for (var p in serializedData)
247                 
if (serializedData.hasOwnProperty(p))
248                     formdata.append(p, serializedData[p]);
249         }
250
251         options.data =
null;
252
253         
var s = $.extend(true, {}, $.ajaxSettings, options, {
254             contentType:
false,
255             processData:
false,
256             cache:
false,
257             type: method ||
'POST'
258         });
259         
260         
if (options.uploadProgress) {
261             
// workaround because jqXHR does not expose upload property
262             s.xhr = function() {
263                 
var xhr = jQuery.ajaxSettings.xhr();
264                 
if (xhr.upload) {
265                     xhr.upload.onprogress = function(
event) {
266                         
var percent = 0;
267                         
var position = event.loaded || event.position; /*event.position is deprecated*/
268                         
var total = event.total;
269                         
if (event.lengthComputable) {
270                             percent = Math.ceil(position / total *
100);
271                         }
272                         options.uploadProgress(
event, position, total, percent);
273                     };
274                 }
275                 
return xhr;
276             };
277         }
278
279         s.data =
null;
280             
var beforeSend = s.beforeSend;
281             s.beforeSend = function(xhr, o) {
282                 o.data = formdata;
283                 
if(beforeSend)
284                     beforeSend.call(
this, xhr, o);
285         };
286         
return $.ajax(s);
287     }
288
289     
// private function for handling file uploads (hat tip to YAHOO!)
290     function fileUploadIframe(a) {
291         
var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
292         
var useProp = !!$.fn.prop;
293         
var deferred = $.Deferred();
294
295         
if ($('[name=submit],[id=submit]', form).length) {
296             
// if there is an input with a name or id of 'submit' then we won't be
297             
// able to invoke the submit fn on the form (at least not x-browser)
298             alert(
'Error: Form elements must not have name or id of "submit".');
299             deferred.reject();
300             
return deferred;
301         }
302         
303         
if (a) {
304             
// ensure that every serialized input is still enabled
305             
for (i=0; i < elements.length; i++) {
306                 el = $(elements[i]);
307                 
if ( useProp )
308                     el.prop(
'disabled', false);
309                 
else
310                     el.removeAttr(
'disabled');
311             }
312         }
313
314         s = $.extend(
true, {}, $.ajaxSettings, options);
315         s.context = s.context || s;
316         id =
'jqFormIO' + (new Date().getTime());
317         
if (s.iframeTarget) {
318             $io = $(s.iframeTarget);
319             n = $io.attr(
'name');
320             
if (!n)
321                  $io.attr(
'name', id);
322             
else
323                 id = n;
324         }
325         
else {
326             $io = $(
'<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
327             $io.css({ position:
'absolute', top: '-1000px', left: '-1000px' });
328         }
329         io = $io[
0];
330
331
332         xhr = {
// mock object
333             aborted:
0,
334             responseText:
null,
335             responseXML:
null,
336             status:
0,
337             statusText:
'n/a',
338             getAllResponseHeaders: function() {},
339             getResponseHeader: function() {},
340             setRequestHeader: function() {},
341             abort: function(status) {
342                 
var e = (status === 'timeout' ? 'timeout' : 'aborted');
343                 log(
'aborting upload... ' + e);
344                 
this.aborted = 1;
345
346                 
try { // #214, #257
347                     
if (io.contentWindow.document.execCommand) {
348                         io.contentWindow.document.execCommand(
'Stop');
349                     }
350                 }
351                 
catch(ignore) {}
352
353                 $io.attr(
'src', s.iframeSrc); // abort op in progress
354                 xhr.error = e;
355                 
if (s.error)
356                     s.error.call(s.context, xhr, e, status);
357                 
if (g)
358                     $.
event.trigger("ajaxError", [xhr, s, e]);
359                 
if (s.complete)
360                     s.complete.call(s.context, xhr, e);
361             }
362         };
363
364         g = s.
global;
365         
// trigger ajax global events so that activity/block indicators work like normal
366         
if (g && 0 === $.active++) {
367             $.
event.trigger("ajaxStart");
368         }
369         
if (g) {
370             $.
event.trigger("ajaxSend", [xhr, s]);
371         }
372
373         
if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
374             
if (s.global) {
375                 $.active--;
376             }
377             deferred.reject();
378             
return deferred;
379         }
380         
if (xhr.aborted) {
381             deferred.reject();
382             
return deferred;
383         }
384
385         
// add submitting element to data if we know it
386         sub = form.clk;
387         
if (sub) {
388             n = sub.name;
389             
if (n && !sub.disabled) {
390                 s.extraData = s.extraData || {};
391                 s.extraData[n] = sub.
value;
392                 
if (sub.type == "image") {
393                     s.extraData[n+
'.x'] = form.clk_x;
394                     s.extraData[n+
'.y'] = form.clk_y;
395                 }
396             }
397         }
398         
399         
var CLIENT_TIMEOUT_ABORT = 1;
400         
var SERVER_ABORT = 2;
401
402         function getDoc(frame) {
403             
var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
404             
return doc;
405         }
406         
407         
// Rails CSRF hack (thanks to Yvan Barthelemy)
408         
var csrf_token = $('meta[name=csrf-token]').attr('content');
409         
var csrf_param = $('meta[name=csrf-param]').attr('content');
410         
if (csrf_param && csrf_token) {
411             s.extraData = s.extraData || {};
412             s.extraData[csrf_param] = csrf_token;
413         }
414
415         
// take a breath so that pending repaints get some cpu time before the upload starts
416         function doSubmit() {
417             
// make sure form attrs are set
418             
var t = $form.attr('target'), a = $form.attr('action');
419
420             
// update form attrs in IE friendly way
421             form.setAttribute(
'target',id);
422             
if (!method) {
423                 form.setAttribute(
'method', 'POST');
424             }
425             
if (a != s.url) {
426                 form.setAttribute(
'action', s.url);
427             }
428
429             
// ie borks in some cases when setting encoding
430             
if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
431                 $form.attr({
432                     encoding:
'multipart/form-data',
433                     enctype:
'multipart/form-data'
434                 });
435             }
436
437             
// support timout
438             
if (s.timeout) {
439                 timeoutHandle = setTimeout(function() { timedOut =
true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
440             }
441             
442             
// look for server aborts
443             function checkState() {
444                 
try {
445                     
var state = getDoc(io).readyState;
446                     log(
'state = ' + state);
447                     
if (state && state.toLowerCase() == 'uninitialized')
448                         setTimeout(checkState,
50);
449                 }
450                 
catch(e) {
451                     log(
'Server abort: ' , e, ' (', e.name, ')');
452                     cb(SERVER_ABORT);
453                     
if (timeoutHandle)
454                         clearTimeout(timeoutHandle);
455                     timeoutHandle = undefined;
456                 }
457             }
458
459             
// add "extra" data to form if provided in options
460             
var extraInputs = [];
461             
try {
462                 
if (s.extraData) {
463                     
for (var n in s.extraData) {
464                         
if (s.extraData.hasOwnProperty(n)) {
465                            
// if using the $.param format that allows for multiple values with the same name
466                            
if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) {
467                                extraInputs.push(
468                                $(
'<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value)
469                                    .appendTo(form)[
0]);
470                            }
else {
471                                extraInputs.push(
472                                $(
'<input type="hidden" name="'+n+'">').val(s.extraData[n])
473                                    .appendTo(form)[
0]);
474                            }
475                         }
476                     }
477                 }
478
479                 
if (!s.iframeTarget) {
480                     
// add iframe to doc and submit the form
481                     $io.appendTo(
'body');
482                     
if (io.attachEvent)
483                         io.attachEvent(
'onload', cb);
484                     
else
485                         io.addEventListener(
'load', cb, false);
486                 }
487                 setTimeout(checkState,
15);
488                 form.submit();
489             }
490             
finally {
491                 
// reset attrs and remove "extra" input elements
492                 form.setAttribute(
'action',a);
493                 
if(t) {
494                     form.setAttribute(
'target', t);
495                 }
else {
496                     $form.removeAttr(
'target');
497                 }
498                 $(extraInputs).
remove();
499             }
500         }
501
502         
if (s.forceSync) {
503             doSubmit();
504         }
505         
else {
506             setTimeout(doSubmit,
10); // this lets dom updates render
507         }
508
509         
var data, doc, domCheckCount = 50, callbackProcessed;
510
511         function cb(e) {
512             
if (xhr.aborted || callbackProcessed) {
513                 
return;
514             }
515             
try {
516                 doc = getDoc(io);
517             }
518             
catch(ex) {
519                 log(
'cannot access response document: ', ex);
520                 e = SERVER_ABORT;
521             }
522             
if (e === CLIENT_TIMEOUT_ABORT && xhr) {
523                 xhr.abort(
'timeout');
524                 deferred.reject(xhr,
'timeout');
525                 
return;
526             }
527             
else if (e == SERVER_ABORT && xhr) {
528                 xhr.abort(
'server abort');
529                 deferred.reject(xhr,
'error', 'server abort');
530                 
return;
531             }
532
533             
if (!doc || doc.location.href == s.iframeSrc) {
534                 
// response not received yet
535                 
if (!timedOut)
536                     
return;
537             }
538             
if (io.detachEvent)
539                 io.detachEvent(
'onload', cb);
540             
else
541                 io.removeEventListener(
'load', cb, false);
542
543             
var status = 'success', errMsg;
544             
try {
545                 
if (timedOut) {
546                     
throw 'timeout';
547                 }
548
549                 
var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
550                 log(
'isXml='+isXml);
551                 
if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
552                     
if (--domCheckCount) {
553                         
// in some browsers (Opera) the iframe DOM is not always traversable when
554                         
// the onload callback fires, so we loop a bit to accommodate
555                         log(
'requeing onLoad callback, DOM not available');
556                         setTimeout(cb,
250);
557                         
return;
558                     }
559                     
// let this fall through because server response could be an empty document
560                     
//log('Could not access iframe DOM after mutiple tries.');
561                     
//throw 'DOMException: not available';
562                 }
563
564                 
//log('response detected');
565                 
var docRoot = doc.body ? doc.body : doc.documentElement;
566                 xhr.responseText = docRoot ? docRoot.innerHTML :
null;
567                 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
568                 
if (isXml)
569                     s.dataType =
'xml';
570                 xhr.getResponseHeader = function(header){
571                     
var headers = {'content-type': s.dataType};
572                     
return headers[header];
573                 };
574                 
// support for XHR 'status' & 'statusText' emulation :
575                 
if (docRoot) {
576                     xhr.status = Number( docRoot.getAttribute(
'status') ) || xhr.status;
577                     xhr.statusText = docRoot.getAttribute(
'statusText') || xhr.statusText;
578                 }
579
580                 
var dt = (s.dataType || '').toLowerCase();
581                 
var scr = /(json|script|text)/.test(dt);
582                 
if (scr || s.textarea) {
583                     
// see if user embedded response in textarea
584                     
var ta = doc.getElementsByTagName('textarea')[0];
585                     
if (ta) {
586                         xhr.responseText = ta.
value;
587                         
// support for XHR 'status' & 'statusText' emulation :
588                         xhr.status = Number( ta.getAttribute(
'status') ) || xhr.status;
589                         xhr.statusText = ta.getAttribute(
'statusText') || xhr.statusText;
590                     }
591                     
else if (scr) {
592                         
// account for browsers injecting pre around json response
593                         
var pre = doc.getElementsByTagName('pre')[0];
594                         
var b = doc.getElementsByTagName('body')[0];
595                         
if (pre) {
596                             xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
597                         }
598                         
else if (b) {
599                             xhr.responseText = b.textContent ? b.textContent : b.innerText;
600                         }
601                     }
602                 }
603                 
else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
604                     xhr.responseXML = toXml(xhr.responseText);
605                 }
606
607                 
try {
608                     data = httpData(xhr, dt, s);
609                 }
610                 
catch (e) {
611                     status =
'parsererror';
612                     xhr.error = errMsg = (e || status);
613                 }
614             }
615             
catch (e) {
616                 log(
'error caught: ',e);
617                 status =
'error';
618                 xhr.error = errMsg = (e || status);
619             }
620
621             
if (xhr.aborted) {
622                 log(
'upload aborted');
623                 status =
null;
624             }
625
626             
if (xhr.status) { // we've set xhr.status
627                 status = (xhr.status >=
200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
628             }
629
630             
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
631             
if (status === 'success') {
632                 
if (s.success)
633                     s.success.call(s.context, data,
'success', xhr);
634                 deferred.resolve(xhr.responseText,
'success', xhr);
635                 
if (g)
636                     $.
event.trigger("ajaxSuccess", [xhr, s]);
637             }
638             
else if (status) {
639                 
if (errMsg === undefined)
640                     errMsg = xhr.statusText;
641                 
if (s.error)
642                     s.error.call(s.context, xhr, status, errMsg);
643                 deferred.reject(xhr,
'error', errMsg);
644                 
if (g)
645                     $.
event.trigger("ajaxError", [xhr, s, errMsg]);
646             }
647
648             
if (g)
649                 $.
event.trigger("ajaxComplete", [xhr, s]);
650
651             
if (g && ! --$.active) {
652                 $.
event.trigger("ajaxStop");
653             }
654
655             
if (s.complete)
656                 s.complete.call(s.context, xhr, status);
657
658             callbackProcessed =
true;
659             
if (s.timeout)
660                 clearTimeout(timeoutHandle);
661
662             
// clean up
663             setTimeout(function() {
664                 
if (!s.iframeTarget)
665                     $io.
remove();
666                 xhr.responseXML =
null;
667             },
100);
668         }
669
670         
var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
671             
if (window.ActiveXObject) {
672                 doc =
new ActiveXObject('Microsoft.XMLDOM');
673                 doc.
async = 'false';
674                 doc.loadXML(s);
675             }
676             
else {
677                 doc = (
new DOMParser()).parseFromString(s, 'text/xml');
678             }
679             
return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
680         };
681         
var parseJSON = $.parseJSON || function(s) {
682             
/*jslint evil:true */
683             
return window['eval']('(' + s + ')');
684         };
685
686         
var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
687
688             
var ct = xhr.getResponseHeader('content-type') || '',
689                 xml = type ===
'xml' || !type && ct.indexOf('xml') >= 0,
690                 data = xml ? xhr.responseXML : xhr.responseText;
691
692             
if (xml && data.documentElement.nodeName === 'parsererror') {
693                 
if ($.error)
694                     $.error(
'parsererror');
695             }
696             
if (s && s.dataFilter) {
697                 data = s.dataFilter(data, type);
698             }
699             
if (typeof data === 'string') {
700                 
if (type === 'json' || !type && ct.indexOf('json') >= 0) {
701                     data = parseJSON(data);
702                 }
else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
703                     $.globalEval(data);
704                 }
705             }
706             
return data;
707         };
708
709         
return deferred;
710     }
711 };

712
713 /**
714  * ajaxForm() provides a mechanism
for fully automating form submission.
715  *
716  * The advantages of
using this method instead of ajaxSubmit() are:
717  *
718  *
1: This method will include coordinates for <input type="image" /> elements (if the element
719  *
is used to submit the form).
720  *
2. This method will include the submit element's name/value data (for the element that was
721  * used to submit the form).
722  *
3. This method binds the submit() method to the form for you.
723  *
724  * The options argument
for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
725  * passes the options argument along after properly binding events
for submit elements and
726  * the form itself.
727  */

728 $.fn.ajaxForm = function(options) {
729     options = options || {};
730     options.delegation = options.delegation && $.isFunction($.fn.
on);
731     
732     
// in jQuery 1.3+ we can fix mistakes with the ready state
733     
if (!options.delegation && this.length === 0) {
734         
var o = { s: this.selector, c: this.context };
735         
if (!$.isReady && o.s) {
736             log(
'DOM not ready, queuing ajaxForm');
737             $(function() {
738                 $(o.s,o.c).ajaxForm(options);
739             });
740             
return this;
741         }
742         
// is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
743         log(
'terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
744         
return this;
745     }
746
747     
if ( options.delegation ) {
748         $(document)
749             .off(
'submit.form-plugin', this.selector, doAjaxSubmit)
750             .off(
'click.form-plugin', this.selector, captureSubmittingElement)
751             .
on('submit.form-plugin', this.selector, options, doAjaxSubmit)
752             .
on('click.form-plugin', this.selector, options, captureSubmittingElement);
753         
return this;
754     }
755
756     
return this.ajaxFormUnbind()
757         .bind(
'submit.form-plugin', options, doAjaxSubmit)
758         .bind(
'click.form-plugin', options, captureSubmittingElement);
759 };

760
761 //
private event handlers
762 function doAjaxSubmit(e) {
763     
/*jshint validthis:true */
764     
var options = e.data;
765     
if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
766         e.preventDefault();
767         $(
this).ajaxSubmit(options);
768     }
769 }
770     
771 function captureSubmittingElement(e) {
772     
/*jshint validthis:true */
773     
var target = e.target;
774     
var $el = $(target);
775     
if (!($el.is("[type=submit],[type=image]"))) {
776         
// is this a child element of the submit el? (ex: a span within a button)
777         
var t = $el.closest('[type=submit]');
778         
if (t.length === 0) {
779             
return;
780         }
781         target = t[
0];
782     }
783     
var form = this;
784     form.clk = target;
785     
if (target.type == 'image') {
786         
if (e.offsetX !== undefined) {
787             form.clk_x = e.offsetX;
788             form.clk_y = e.offsetY;
789         }
else if (typeof $.fn.offset == 'function') {
790             
var offset = $el.offset();
791             form.clk_x = e.pageX - offset.left;
792             form.clk_y = e.pageY - offset.top;
793         }
else {
794             form.clk_x = e.pageX - target.offsetLeft;
795             form.clk_y = e.pageY - target.offsetTop;
796         }
797     }
798     
// clear form vars
799     setTimeout(function() { form.clk = form.clk_x = form.clk_y =
null; }, 100);
800 }

801
802
803 // ajaxFormUnbind unbinds the
event handlers that were bound by ajaxForm
804 $.fn.ajaxFormUnbind = function() {
805     
return this.unbind('submit.form-plugin click.form-plugin');
806 };

807
808 /**
809  * formToArray() gathers form element data
into an array of objects that can
810  * be passed to any of the following ajax functions: $.
get, $.post, or load.
811  * Each
object in the array has both a 'name' and 'value' property. An example of
812  * an array
for a simple login form might be:
813  *
814  * [ { name:
'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
815  *
816  * It
is this array that is passed to pre-submit callback functions provided to the
817  * ajaxSubmit() and ajaxForm() methods.
818  */

819 $.fn.formToArray = function(semantic, elements) {
820     
var a = [];
821     
if (this.length === 0) {
822         
return a;
823     }
824
825     
var form = this[0];
826     
var els = semantic ? form.getElementsByTagName('*') : form.elements;
827     
if (!els) {
828         
return a;
829     }
830
831     
var i,j,n,v,el,max,jmax;
832     
for(i=0, max=els.length; i < max; i++) {
833         el = els[i];
834         n = el.name;
835         
if (!n) {
836             
continue;
837         }
838
839         
if (semantic && form.clk && el.type == "image") {
840             
// handle image inputs on the fly when semantic == true
841             
if(!el.disabled && form.clk == el) {
842                 a.push({name: n,
value: $(el).val(), type: el.type });
843                 a.push({name: n+
'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
844             }
845             
continue;
846         }
847
848         v = $.fieldValue(el,
true);
849         
if (v && v.constructor == Array) {
850             
if (elements)
851                 elements.push(el);
852             
for(j=0, jmax=v.length; j < jmax; j++) {
853                 a.push({name: n,
value: v[j]});
854             }
855         }
856         
else if (feature.fileapi && el.type == 'file' && !el.disabled) {
857             
if (elements)
858                 elements.push(el);
859             
var files = el.files;
860             
if (files.length) {
861                 
for (j=0; j < files.length; j++) {
862                     a.push({name: n,
value: files[j], type: el.type});
863                 }
864             }
865             
else {
866                 
// #180
867                 a.push({ name: n,
value: '', type: el.type });
868             }
869         }
870         
else if (v !== null && typeof v != 'undefined') {
871             
if (elements)
872                 elements.push(el);
873             a.push({name: n,
value: v, type: el.type, required: el.required});
874         }
875     }
876
877     
if (!semantic && form.clk) {
878         
// input type=='image' are not found in elements array! handle it here
879         
var $input = $(form.clk), input = $input[0];
880         n = input.name;
881         
if (n && !input.disabled && input.type == 'image') {
882             a.push({name: n,
value: $input.val()});
883             a.push({name: n+
'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
884         }
885     }
886     
return a;
887 };

888
889 /**
890  * Serializes form data
into a 'submittable' string. This method will return a string
891  *
in the format: name1=value1&amp;name2=value2
892  */

893 $.fn.formSerialize = function(semantic) {
894     
//hand off to jQuery.param for proper encoding
895     
return $.param(this.formToArray(semantic));
896 };

897
898 /**
899  * Serializes all field elements
in the jQuery object into a query string.
900  * This method will
return a string in the format: name1=value1&amp;name2=value2
901  */

902 $.fn.fieldSerialize = function(successful) {
903     
var a = [];
904     
this.each(function() {
905         
var n = this.name;
906         
if (!n) {
907             
return;
908         }
909         
var v = $.fieldValue(this, successful);
910         
if (v && v.constructor == Array) {
911             
for (var i=0,max=v.length; i < max; i++) {
912                 a.push({name: n,
value: v[i]});
913             }
914         }
915         
else if (v !== null && typeof v != 'undefined') {
916             a.push({name:
this.name, value: v});
917         }
918     });
919     
//hand off to jQuery.param for proper encoding
920     
return $.param(a);
921 };

922
923 /**
924  * Returns the
value(s) of the element in the matched set. For example, consider the following form:
925  *
926  * <form><fieldset>
927  * <input name=
"A" type="text" />
928  * <input name=
"A" type="text" />
929  * <input name=
"B" type="checkbox" value="B1" />
930  * <input name=
"B" type="checkbox" value="B2"/>
931  * <input name=
"C" type="radio" value="C1" />
932  * <input name=
"C" type="radio" value="C2" />
933  * </fieldset></form>
934  *
935  *
var v = $('input[type=text]').fieldValue();
936  * //
if no values are entered into the text inputs
937  * v == [
'','']
938  * //
if values entered into the text inputs are 'foo' and 'bar'
939  * v == [
'foo','bar']
940  *
941  *
var v = $('input[type=checkbox]').fieldValue();
942  * //
if neither checkbox is checked
943  * v === undefined
944  * //
if both checkboxes are checked
945  * v == [
'B1', 'B2']
946  *
947  *
var v = $('input[type=radio]').fieldValue();
948  * //
if neither radio is checked
949  * v === undefined
950  * //
if first radio is checked
951  * v == [
'C1']
952  *
953  * The successful argument controls whether or not the field element must be
'successful'
954  * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
955  * The
default value of the successful argument is true. If this value is false the value(s)
956  *
for each element is returned.
957  *
958  * Note: This method *always* returns an array. If no valid
value can be determined the
959  * array will be empty, otherwise it will contain one or more values.
960  */

961 $.fn.fieldValue = function(successful) {
962     
for (var val=[], i=0, max=this.length; i < max; i++) {
963         
var el = this[i];
964         
var v = $.fieldValue(el, successful);
965         
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
966             
continue;
967         }
968         
if (v.constructor == Array)
969             $.merge(val, v);
970         
else
971             val.push(v);
972     }
973     
return val;
974 };

975
976 /**
977  * Returns the
value of the field element.
978  */

979 $.fieldValue = function(el, successful) {
980     
var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
981     
if (successful === undefined) {
982         successful =
true;
983     }
984
985     
if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
986         (t ==
'checkbox' || t == 'radio') && !el.checked ||
987         (t ==
'submit' || t == 'image') && el.form && el.form.clk != el ||
988         tag ==
'select' && el.selectedIndex == -1)) {
989             
return null;
990     }
991
992     
if (tag == 'select') {
993         
var index = el.selectedIndex;
994         
if (index < 0) {
995             
return null;
996         }
997         
var a = [], ops = el.options;
998         
var one = (t == 'select-one');
999         
var max = (one ? index+1 : ops.length);
1000         
for(var i=(one ? index : 0); i < max; i++) {
1001             
var op = ops[i];
1002             
if (op.selected) {
1003                 
var v = op.value;
1004                 
if (!v) { // extra pain for IE...
1005                     v = (op.attributes && op.attributes[
'value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
1006                 }
1007                 
if (one) {
1008                     
return v;
1009                 }
1010                 a.push(v);
1011             }
1012         }
1013         
return a;
1014     }
1015     
return $(el).val();
1016 };

1017
1018 /**
1019  * Clears the form data. Takes the following actions
on the form's input fields:
1020  * - input text fields will have their
'value' property set to the empty string
1021  * -
select elements will have their 'selectedIndex' property set to -1
1022  * - checkbox and radio inputs will have their
'checked' property set to false
1023  * - inputs of type submit, button, reset, and hidden will *not* be effected
1024  * - button elements will *not* be effected
1025  */

1026 $.fn.clearForm = function(includeHidden) {
1027     
return this.each(function() {
1028         $(
'input,select,textarea', this).clearFields(includeHidden);
1029     });
1030 };

1031
1032 /**
1033  * Clears the selected form elements.
1034  */

1035 $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
1036     
var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
1037     
return this.each(function() {
1038         
var t = this.type, tag = this.tagName.toLowerCase();
1039         
if (re.test(t) || tag == 'textarea') {
1040             
this.value = '';
1041         }
1042         
else if (t == 'checkbox' || t == 'radio') {
1043             
this.checked = false;
1044         }
1045         
else if (tag == 'select') {
1046             
this.selectedIndex = -1;
1047         }
1048         
else if (t == "file") {
1049             
if ($.browser.msie) {
1050                 $(
this).replaceWith($(this).clone());
1051             }
else {
1052                 $(
this).val('');
1053             }
1054         }
1055         
else if (includeHidden) {
1056             
// includeHidden can be the value true, or it can be a selector string
1057             
// indicating a special test; for example:
1058             
// $('#myForm').clearForm('.special:hidden')
1059             
// the above would clean hidden inputs that have the class of 'special'
1060             
if ( (includeHidden === true && /hidden/.test(t)) ||
1061                  (
typeof includeHidden == 'string' && $(this).is(includeHidden)) )
1062                 
this.value = '';
1063         }
1064     });
1065 };

1066
1067 /**
1068  * Resets the form data. Causes all form elements to be reset to their original
value.
1069  */

1070 $.fn.resetForm = function() {
1071     
return this.each(function() {
1072         
// guard against an input with the name of 'reset'
1073         
// note that IE reports the reset function as an 'object'
1074         
if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
1075             
this.reset();
1076         }
1077     });
1078 };

1079
1080 /**
1081  * Enables or disables any matching elements.
1082  */

1083 $.fn.enable = function(b) {
1084     
if (b === undefined) {
1085         b =
true;
1086     }
1087     
return this.each(function() {
1088         
this.disabled = !b;
1089     });
1090 };

1091
1092 /**
1093  * Checks/unchecks any matching checkboxes or radio buttons and
1094  * selects/deselects and matching option elements.
1095  */

1096 $.fn.selected = function(
select) {
1097     
if (select === undefined) {
1098         
select = true;
1099     }
1100     
return this.each(function() {
1101         
var t = this.type;
1102         
if (t == 'checkbox' || t == 'radio') {
1103             
this.checked = select;
1104         }
1105         
else if (this.tagName.toLowerCase() == 'option') {
1106             
var $sel = $(this).parent('select');
1107             
if (select && $sel[0] && $sel[0].type == 'select-one') {
1108                 
// deselect all other options
1109                 $sel.find(
'option').selected(false);
1110             }
1111             
this.selected = select;
1112         }
1113     });
1114 };

1115
1116 // expose debug
var
1117 $.fn.ajaxSubmit.debug =
false;
1118
1119 // helper fn
for console logging
1120 function log() {
1121     
if (!$.fn.ajaxSubmit.debug)
1122         
return;
1123     
var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1124     
if (window.console && window.console.log) {
1125         window.console.log(msg);
1126     }
1127     
else if (window.opera && window.opera.postError) {
1128         window.opera.postError(msg);
1129     }
1130 }
1131
1132 })(jQuery);



Full source code website bán hàng thương mại điện tử gần giống shopee 472.077 lượt xem

Gõ tìm kiếm nhanh...